package com.gitee.wsl.mathematics.matrix.mat4.transform

import com.gitee.wsl.mathematics.matrix.mat4.Mat4f
import com.gitee.wsl.mathematics.matrix.mat4.Matrix4
import com.gitee.wsl.mathematics.matrix.mat4.MutableMat4f
import com.gitee.wsl.mathematics.matrix.mat4.MutableMatrix4
import com.gitee.wsl.mathematics.vector.vec4.Vector4
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmName
import kotlin.reflect.KMutableProperty0


/**
 * Build a transform presented by a Matrix44
 * @param baseTransform the transform to start with, default is an identity matrix
 * @param builder a function that is invoke inside the [TransformBuilder] context
 */
@OptIn(ExperimentalContracts::class)
fun<T:Number,V:Vector4<T,V>,R:Matrix4<T,R,V>> transform(baseTransform: R , builder: MutableMatrix4<T, R,V>.() -> Unit): MutableMatrix4<T, R, V> {
    contract {
        callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
    }
    val mutable = baseTransform.toMutable()
    builder(mutable)
    return mutable
}

/**
 * An alias for transform, useful for those cases in which the word transform is used too often
 */
/*@OptIn(ExperimentalContracts::class)
fun buildTransform(baseTransform: Mat4 = Mat4.IDENTITY, builder: MutableMat4.() -> Unit): Mat4{
    contract {
        callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
    }
    val mutable = baseTransform.toMutable()
    builder(mutable)
    return mutable
}*/

@OptIn(ExperimentalContracts::class)
fun buildTransform(baseTransform: Mat4f = MutableMat4f.IDENTITY, builder: MutableMat4f.() -> Unit): Mat4f {
    contract {
        callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
    }
    val mutable = baseTransform.toMutable()
    builder(mutable)
    return mutable
}

/**
 * Matrix44 transform helper
 */
@OptIn(ExperimentalContracts::class)
@JvmName("matrix44Transform")
fun<T:Number,V:Vector4<T,V>,R:Matrix4<T,R,V>>  R.transform( builder: MutableMatrix4<T, R,V>.() -> Unit)
   = transform(baseTransform = this, builder = builder)


/**
 * Matrix44 property transform helper
 */
@OptIn(ExperimentalContracts::class)
fun KMutableProperty0<Mat4f>.transform(builder: MutableMat4f.() -> Unit) {
    contract {
        callsInPlace(builder, InvocationKind.EXACTLY_ONCE)
    }
    set(buildTransform(get(),builder))
}